#include "GameMap.h"

#include <cstdlib>
#include <ctime>
#include <list>
#include <iostream>

namespace catan
{

GameMap::GameMap()
{
	reset();
}

void GameMap::reset()
{
	cells = std::map<CellCoordinate, Cell>();
	sites = std::map<SiteCoordinate, Site>();
	paths = std::map<PathCoordinate, Path>();
	std::vector<CellType::Enum_CellType> landTypeEnumValues = CellType::getVectorOfLandEnumValues();
	std::vector<CellType::Enum_CellType> harborTypeEnumValues = CellType::getVectorOfHarborEnumValues();
	std::vector<CellNumber::Enum_CellNumber> nonDesertLandNumberEnumValues = CellNumber::getVectorOfNonDesertLandEnumValues();
	int randomIndex;
	srand((unsigned)time(0));
	bool isHarbor = ((rand() % 2) == 0);
	const std::vector<CoordinateX::Enum_CoordinateX> enumCoordinateXs = CoordinateX::getVectorOfPossibleEnumValues();
	const std::vector<CoordinateY::Enum_CoordinateY> enumCoordinateYs = CoordinateY::getVectorOfPossibleEnumValues();
	for (std::vector<CoordinateY::Enum_CoordinateY>::const_iterator enumCoordinateYIterator = enumCoordinateYs.begin(); enumCoordinateYIterator != enumCoordinateYs.end(); ++enumCoordinateYIterator)
	{
		for (std::vector<CoordinateX::Enum_CoordinateX>::const_iterator enumCoordinateXIterator = enumCoordinateXs.begin(); enumCoordinateXIterator != enumCoordinateXs.end(); ++enumCoordinateXIterator)
		{
			CoordinateX coordinateX = CoordinateX(*enumCoordinateXIterator);
			CoordinateY coordinateY = CoordinateY(*enumCoordinateYIterator);
			if (CellCoordinate::canInit(coordinateX, coordinateY))
			{
				CellCoordinate cellCoordinate(coordinateX, coordinateY);
				if (Cell::isLand(cellCoordinate))
				{
					randomIndex = static_cast<int>(rand() % landTypeEnumValues.size());
					CellType::Enum_CellType landTypeEnumValue = landTypeEnumValues[randomIndex];
					int lastElementIndex = static_cast<int>(landTypeEnumValues.size()-1);
					landTypeEnumValues[randomIndex] = landTypeEnumValues[lastElementIndex];
					landTypeEnumValues.pop_back();
					if (landTypeEnumValue == CellType::DESERT_TYPE)
					{
						cells.insert(std::make_pair(cellCoordinate, Cell(cellCoordinate, CellType(landTypeEnumValue), catan::n7_Desert)));
					}
					else
					{
						randomIndex = static_cast<int>(rand() % nonDesertLandNumberEnumValues.size());
						CellNumber::Enum_CellNumber nonDesertLandNumberEnumValue = nonDesertLandNumberEnumValues[randomIndex];
						int lastElementIndex = static_cast<int>(nonDesertLandNumberEnumValues.size()-1);
						nonDesertLandNumberEnumValues[randomIndex] = nonDesertLandNumberEnumValues[lastElementIndex];
						nonDesertLandNumberEnumValues.pop_back();
						cells.insert(std::make_pair(cellCoordinate, Cell(cellCoordinate, CellType(landTypeEnumValue), CellNumber(nonDesertLandNumberEnumValue))));
					}
				}
				else
				{
					if (isHarbor)
					{
						randomIndex = static_cast<int>(rand() % harborTypeEnumValues.size());
						CellType::Enum_CellType harborTypeEnumValue = harborTypeEnumValues[randomIndex];
						int lastElementIndex = static_cast<int>(harborTypeEnumValues.size()-1);
						harborTypeEnumValues[randomIndex] = harborTypeEnumValues[lastElementIndex];
						harborTypeEnumValues.pop_back();
						cells.insert(std::make_pair(cellCoordinate, Cell(cellCoordinate, CellType(harborTypeEnumValue), catan::nSea)));
					}
					else
					{
						cells.insert(std::make_pair(cellCoordinate, Cell(cellCoordinate, catan::openSeaType, catan::nSea)));
					}
					isHarbor = !isHarbor;
				}
			}
			
			const std::vector<VerticalCardinal::Enum_VerticalCardinal> enumVerticalCardinals = VerticalCardinal::getVectorOfReducedPossibleEnumValues();
			for (std::vector<VerticalCardinal::Enum_VerticalCardinal>::const_iterator enumVerticalCardinalIterator = enumVerticalCardinals.begin(); enumVerticalCardinalIterator != enumVerticalCardinals.end(); ++enumVerticalCardinalIterator)
			{
				if (PathCoordinate::canInit(coordinateX, coordinateY, VerticalCardinal(*enumVerticalCardinalIterator)))
				{
					PathCoordinate pathCoordinate(coordinateX, coordinateY, VerticalCardinal(*enumVerticalCardinalIterator));
					Path path(pathCoordinate);
					paths.insert(std::make_pair(pathCoordinate, path));
				}
			}
			
			const std::vector<HorizontalCardinal::Enum_HorizontalCardinal> enumHorizontalCardinals = HorizontalCardinal::getVectorOfReducedPossibleEnumValues();
			for (std::vector<HorizontalCardinal::Enum_HorizontalCardinal>::const_iterator enumHorizontalCardinalIterator = enumHorizontalCardinals.begin(); enumHorizontalCardinalIterator != enumHorizontalCardinals.end(); ++enumHorizontalCardinalIterator)
			{
				//SiteCoordinate::Enum_SiteCoordinate_Cardinal cardinal = siteCardinals[k];
				if (SiteCoordinate::canInit(coordinateX, coordinateY, HorizontalCardinal(*enumHorizontalCardinalIterator)))
				{
					SiteCoordinate siteCoordinate(coordinateX, coordinateY, HorizontalCardinal(*enumHorizontalCardinalIterator));
					Site site(siteCoordinate);
					sites.insert(std::make_pair(siteCoordinate, site));
				}
			}
		}
		isHarbor = !isHarbor;
	}
}

Cell &GameMap::getCell(CellCoordinate cellCoordinate)
{
	return cells.find(cellCoordinate)->second;
}

Path &GameMap::getPath(PathCoordinate pathCoordinate)
{
	return paths.find(pathCoordinate)->second;
}

Site &GameMap::getSite(SiteCoordinate siteCoordinate)
{
	return sites.find(siteCoordinate)->second;
}

std::map<VerticalCardinal, Path*> GameMap::getPathsNeighboringCell(Cell cell)
{
	// Conversion shorthand (read conversion.txt for more)
	// 
	// .*:
    //     [v-tr] = +0 -> .v-tr
	//     [v-tc] = +0 -> .v-tc //auto-convert
	//     [v-tl] = +0 -> .v-tl
	//     [v-bl] = +0 -> .v-bl //auto-convert
	//     [v-bc] = +0 -> .v-bc
	//     [v-br] = +0 -> .v-br //auto-convert
	
	std::map<VerticalCardinal, Path*> result;
	const std::vector<VerticalCardinal::Enum_VerticalCardinal> vectorOfPossibleVerticalCardinalEnumValues = VerticalCardinal::getVectorOfPossibleEnumValues();
	CellCoordinate cellCoordinate = cell.getCoordinate();
	for (std::vector<VerticalCardinal::Enum_VerticalCardinal>::const_iterator enumPossibleVerticalCardinalIterator = vectorOfPossibleVerticalCardinalEnumValues.begin(); enumPossibleVerticalCardinalIterator != vectorOfPossibleVerticalCardinalEnumValues.end(); ++enumPossibleVerticalCardinalIterator)
	{
		if (PathCoordinate::canInit(cellCoordinate.getCoordinateX(), cellCoordinate.getCoordinateY(), VerticalCardinal(*enumPossibleVerticalCardinalIterator)))
		{
			PathCoordinate pathCoordinate(cellCoordinate.getCoordinateX(), cellCoordinate.getCoordinateY(), VerticalCardinal(*enumPossibleVerticalCardinalIterator));
			result.insert(std::make_pair(*enumPossibleVerticalCardinalIterator, &getPath(pathCoordinate)));
		}
	}
	return result;
}

std::map<HorizontalCardinal, Site*> GameMap::getSitesNeighboringCell(Cell cell)
{
	// Conversion shorthand (read conversion.txt for more)
	// 
	// *:
	//     [h-cr] = +0 -> .h-cr
	//     [h-tr] = +0 -> .h-tr //auto-convert
	//     [h-tl] = +0 -> .h-tl //auto-convert
	//     [h-cl] = +0 -> .h-cl
	//     [h-bl] = +0 -> .h-bl //auto-convert
	//     [h-br] = +0 -> .h-br //auto-convert

	std::map<HorizontalCardinal, Site*> result;
	const std::vector<HorizontalCardinal::Enum_HorizontalCardinal> vectorOfPossibleHorizontalCardinalEnumValues = HorizontalCardinal::getVectorOfPossibleEnumValues();
	CellCoordinate cellCoordinate = cell.getCoordinate();
	for (std::vector<HorizontalCardinal::Enum_HorizontalCardinal>::const_iterator enumPossibleHorizontalCardinalIterator = vectorOfPossibleHorizontalCardinalEnumValues.begin(); enumPossibleHorizontalCardinalIterator != vectorOfPossibleHorizontalCardinalEnumValues.end(); ++enumPossibleHorizontalCardinalIterator)
	{
		if (SiteCoordinate::canInit(cellCoordinate.getCoordinateX(), cellCoordinate.getCoordinateY(), HorizontalCardinal(*enumPossibleHorizontalCardinalIterator)))
		{
			SiteCoordinate siteCoordinate(cellCoordinate.getCoordinateX(), cellCoordinate.getCoordinateY(), HorizontalCardinal(*enumPossibleHorizontalCardinalIterator));
			result.insert(std::make_pair(*enumPossibleHorizontalCardinalIterator, &getSite(siteCoordinate)));
		}
	}
	return result;
}

std::map<VerticalCardinal, Cell*> GameMap::getCellsNeighboringPath(Path path)
{
	// Conversion shorthand (read conversion.txt for more)
	// 
	// .v-tr:
	//     [v-tr] = +v-tr -> .*
	//     [v-bl] =    +0 -> .*
	// .v-tl:
	//     [v-tl] = +v-tl -> .*
	//     [v-br] =    +0 -> .*
	// .v-bc:
	//     [v-tc] =    +0 -> .*
	//     [v-bc] = +v-bc -> .*
	
	std::map<VerticalCardinal, Cell*> result;
	PathCoordinate pathCoordinate = path.getCoordinate();
	CoordinateX coordinateX = pathCoordinate.getCoordinateX();
	CoordinateY coordinateY = pathCoordinate.getCoordinateY();
	CellCoordinate cellCoordinate(coordinateX, coordinateY);
	switch (pathCoordinate.getVerticalCardinal().getEnum())
	{
	case VerticalCardinal::VERTICAL_TR:
		result.insert(std::make_pair(VerticalCardinal::VERTICAL_TR, &getCell(cellCoordinate.createNeighbor(catan::verticalTR))));
		result.insert(std::make_pair(VerticalCardinal::VERTICAL_BL, &getCell(cellCoordinate)));
		break;
	case VerticalCardinal::VERTICAL_TL:
		result.insert(std::make_pair(VerticalCardinal::VERTICAL_TL, &getCell(cellCoordinate.createNeighbor(catan::verticalTL))));
		result.insert(std::make_pair(VerticalCardinal::VERTICAL_BR, &getCell(cellCoordinate)));
		break;
	case VerticalCardinal::VERTICAL_BC:
		result.insert(std::make_pair(VerticalCardinal::VERTICAL_TC, &getCell(cellCoordinate)));
		result.insert(std::make_pair(VerticalCardinal::VERTICAL_BC, &getCell(cellCoordinate.createNeighbor(catan::verticalBC))));
		break;
	default:
		break;
	}
	return result;
}

std::map<HorizontalCardinal, Site*> GameMap::getSitesNeighboringPath(Path path)
{
	// Conversion shorthand (read conversion.txt for more)
	// 
	// .v-tr:
	//     [h-tl] = +0 -> .h-tr //auto-convert
	//     [h-br] = +0 -> .h-cr
	// .v-tl:
	//     [h-tr] = +0 -> .h-tl //auto-convert
	//     [h-bl] = +0 -> .h-cl
	// .v-bc:
	//     [h-cl] = +0 -> .h-bl //auto-convert
	//     [h-cr] = +0 -> .h-br //auto-convert
	
	std::map<HorizontalCardinal, Site*> result;
	PathCoordinate pathCoordinate = path.getCoordinate();
	CoordinateX coordinateX = pathCoordinate.getCoordinateX();
	CoordinateY coordinateY = pathCoordinate.getCoordinateY();
	switch (pathCoordinate.getVerticalCardinal().getEnum())
	{
	case VerticalCardinal::VERTICAL_TR:
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_TL, &getSite(SiteCoordinate(coordinateX, coordinateY, catan::horizontalTR))));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_BR, &getSite(SiteCoordinate(coordinateX, coordinateY, catan::horizontalCR))));
		break;
	case VerticalCardinal::VERTICAL_TL:
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_TR, &getSite(SiteCoordinate(coordinateX, coordinateY, catan::horizontalTL))));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_BL, &getSite(SiteCoordinate(coordinateX, coordinateY, catan::horizontalCL))));
		break;
	case VerticalCardinal::VERTICAL_BC:
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_CL, &getSite(SiteCoordinate(coordinateX, coordinateY, catan::horizontalBL))));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_CR, &getSite(SiteCoordinate(coordinateX, coordinateY, catan::horizontalBR))));
		break;
	default:
		break;
	}
	return result;
}

std::map<HorizontalCardinal, Cell*> GameMap::getCellsNeighboringSite(Site site)
{
	// Conversion shorthand (read conversion.txt for more)
	// 
	// .h-cr:
	//     [h-tr] = +v-tr -> .*
	//     [h-cl] =    +0 -> .*
	//     [h-br] = +v-br -> .*
	// .h-cl:
	//     [h-cr] =    +0 -> .*
	//     [h-tl] = +v-tl -> .*
	//     [h-bl] = +v-bl -> .*
	
	std::map<HorizontalCardinal, Cell*> result;
	SiteCoordinate siteCoordinate = site.getCoordinate();
	CoordinateX coordinateX = siteCoordinate.getCoordinateX();
	CoordinateY coordinateY = siteCoordinate.getCoordinateY();
	CellCoordinate cellCoordinate(coordinateX, coordinateY);
	switch (siteCoordinate.getHorizontalCardinal().getEnum())
	{
	case HorizontalCardinal::HORIZONTAL_CR:
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_TR, &getCell(cellCoordinate.createNeighbor(catan::verticalTR))));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_CL, &getCell(cellCoordinate)));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_BR, &getCell(cellCoordinate.createNeighbor(catan::verticalBR))));
		break;
	case HorizontalCardinal::HORIZONTAL_CL:
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_CR, &getCell(cellCoordinate)));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_TL, &getCell(cellCoordinate.createNeighbor(catan::verticalTL))));
		result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_BL, &getCell(cellCoordinate.createNeighbor(catan::verticalBL))));
		break;
	default:
		break;
	}
	return result;
}

std::map<HorizontalCardinal, Path*> GameMap::getPathsNeighboringSite(Site site)
{
	// Conversion shorthand (read conversion.txt for more)
	// 
	// .h-cr:
	//     [h-cr] = +v-tr -> .v-bc
	//     [h-tl] =    +0 -> .v-tr
	//     [h-bl] =    +0 -> .v-br //auto-convert
	// .h-cl:
	//     [h-tr] =    +0 -> .v-tl
	//     [h-cl] = +v-tl -> .v-bc
	//     [h-br] =    +0 -> .v-bl //auto-convert
	
	std::map<HorizontalCardinal, Path*> result;
	SiteCoordinate siteCoordinate = site.getCoordinate();
	CoordinateX coordinateX = siteCoordinate.getCoordinateX();
	CoordinateY coordinateY = siteCoordinate.getCoordinateY();
	CellCoordinate cellCoordinate(coordinateX, coordinateY);
	CoordinateX movedCoordinateX = coordinateX;
	CoordinateY movedCoordinateY = coordinateY;
	CellCoordinate movedCellCoordinate = cellCoordinate;
	
	switch (siteCoordinate.getHorizontalCardinal().getEnum())
	{
	case HorizontalCardinal::HORIZONTAL_CR:
		movedCellCoordinate = cellCoordinate.createNeighbor(catan::verticalTR);
		movedCoordinateX = movedCellCoordinate.getCoordinateX();
		movedCoordinateY = movedCellCoordinate.getCoordinateY();
		if (PathCoordinate::canInit(movedCoordinateX, movedCoordinateY, catan::verticalBC))
		{
			result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_CR, &getPath(PathCoordinate(movedCoordinateX, movedCoordinateY, catan::verticalBC))));
		}
		if (PathCoordinate::canInit(coordinateX, coordinateY, catan::verticalTR))
		{
			result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_TL, &getPath(PathCoordinate(coordinateX, coordinateY, catan::verticalTR))));
		}
		if (PathCoordinate::canInit(coordinateX, coordinateY, catan::verticalBR))
		{
			result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_BL, &getPath(PathCoordinate(coordinateX, coordinateY, catan::verticalBR))));
		}
		break;
	case HorizontalCardinal::HORIZONTAL_CL:
		movedCellCoordinate = cellCoordinate.createNeighbor(catan::verticalTL);
		movedCoordinateX = movedCellCoordinate.getCoordinateX();
		movedCoordinateY = movedCellCoordinate.getCoordinateY();
		if (PathCoordinate::canInit(coordinateX, coordinateY, catan::verticalTL))
		{
			result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_TR, &getPath(PathCoordinate(coordinateX, coordinateY, catan::verticalTL))));
		}
		if (PathCoordinate::canInit(movedCoordinateX, movedCoordinateY, catan::verticalBC))
		{
			result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_CL, &getPath(PathCoordinate(movedCoordinateX, movedCoordinateY, catan::verticalBC))));
		}
		if (PathCoordinate::canInit(coordinateX, coordinateY, catan::verticalBL))
		{
			result.insert(std::make_pair(HorizontalCardinal::HORIZONTAL_BR, &getPath(PathCoordinate(coordinateX, coordinateY, catan::verticalBL))));
		}
		break;
	default:
		break;
	}
	return result;
}

std::vector<Cell*> GameMap::getCellsOfNumber(CellNumber cellNumber)
{
	std::vector<Cell*> result;
	for (std::map<CellCoordinate, Cell>::iterator enumCellIterator = cells.begin(); enumCellIterator != cells.end(); ++enumCellIterator)
	{
		if (cellNumber == enumCellIterator->second.getNumber())
		{
			result.push_back(&enumCellIterator->second);
		}
	}
	return result;
}

}
